/*********************************************************************************************************
 *  ------------------------------------------------------------------------------------------------------
 *  file description
 *  ------------------------------------------------------------------------------------------------------
 *         \file  vector.h
 *        \brief  This is a general C language vector container module, which can dynamically adjust the size and general data structure
 *       \author  Lamdonn
 *        \email  Lamdonn@163.com
 *      \version  1.1.0
 *      \license  GPL2
 *    \copyright  Copyright (C) 2023 Lamdonn.
 ********************************************************************************************************/
#include "vlib.h"
#include <string.h>

#define IDENT                           (1)

/* tool */
#define up_multiple(x, mul)             ((x)+((mul)-((x)-1)%(mul))-1)

/** gradient_capacity
 *  \brief calculate capacity according to gradient
 *  \param[in] size: size
 *  \return capacity
 */
static int gradient_capacity(int size)
{
    int capacity = 1 << (sizeof(int) * 8 / 2 - 1);

    if (size <= 1) return 1;

    while (capacity >= size) capacity >>= 1;

    if (capacity < 4) capacity = capacity << 1;
    else if (capacity < 16) capacity = up_multiple(size, capacity >> 1);
    else if (capacity < 256) capacity = up_multiple(size, capacity >> 2);
    else capacity = up_multiple(size, 64);
    
    return capacity;
}

vector_t vector_create(int dsize, int size)
{
    vector_t vector = NULL;
    int capacity = 0;

    if (dsize < 0 || size < 0) return NULL;

    /* get the required storage capacity */
    capacity = gradient_capacity(size);

    /* alloc vector */
    vector = (vector_t)vlib_malloc(sizeof(VECTOR));
    if (!vector) return NULL;

    /* alloc data space */
    vector->parent.base = vlib_malloc(dsize * (capacity + 1)); /* the space of one more position is the error area */
    if (vector->parent.base == NULL)
    {
        vlib_free(vector);
        return NULL;
    }

    /* reset data space */
    memset(vector->parent.base, 0, dsize * capacity);

    /* assign parameters */
    vector->parent.ident = IDENT;
    vector->parent.dsize = dsize;
    vector->parent.size = size;
    vector->capacity = capacity;

    return vector;
}

void vector_delete(vector_t vector)
{
    /* validity check */
    if (!vector) return;

    vlib_free(vector->parent.base);
    vlib_free(vector);
}

int vector_assign(vector_t vector, void* data, int num)
{
    /* validity check */
    if (!vector) return 0;
    if (!data) return 0;
    if (num <= 0) return 0;
    
    /* resize */
    if (!vector_resize(vector, num)) return 0;

    /* assign */
    memcpy(vector->parent.base, data, vector->parent.dsize * num);

    return 1;
}

int vector_swap(vector_t vector, vector_t swap)
{
    VECTOR temp;

    /* validity check */
    if (!vector) return 0;
    if (!swap) return 0;

    temp = *vector;
    *vector = *swap;
    *swap = temp;

    return 1;
}

void* vector_data(vector_t vector, int index)
{
    /* validity check */
    if (!vector) return 0;

    /* error data */
    if (index >= vector->parent.size || index < 0)
    {
        return vector_error(vector);
    }
        
    /* correct data */
    return (unsigned char *)(vector->parent.base) + index * vector->parent.dsize;
}

void* vector_error(vector_t vector)
{
    /* validity check */
    if (!vector) return NULL;

    /* error data */
    return (unsigned char *)(vector->parent.base) + vector->capacity * vector->parent.dsize;
}

int vector_resize(vector_t vector, int size)
{
    int capacity = 0;
    void* base = NULL;

    /* validity check */
    if (!vector) return 0;

    if (size < 0)
    {
        return 0;
    }

    /* get new capacity */
    capacity = gradient_capacity(size);

    if (capacity == vector->capacity) /* no capacity adjustment required */
    {
        if (vector->parent.size > size)
        {
            base = ((unsigned char *)(vector->parent.base) + size * (vector->parent.dsize));
            memset(base, 0, (vector->parent.size - size) * vector->parent.dsize);
        }
        else if (size > vector->parent.size)
        {
            base = ((unsigned char *)(vector->parent.base) + vector->parent.size * (vector->parent.dsize));
            memset(base, 0, (size - vector->parent.size) * vector->parent.dsize);
        }

        vector->parent.size = size;
    }
    else /* need to adjust capacity */
    {
        /* re alloc space */
        base = vlib_realloc(vector->parent.base, (capacity + 1) * vector->parent.dsize);
        if (base == NULL)
        {
            return 0;
        }
        
        vector->parent.base = base;
        vector->capacity = capacity;

        /* increase size, reset extension */
        if (size > vector->parent.size)
        {
            base = ((unsigned char *)(vector->parent.base) + vector->parent.size * (vector->parent.dsize));
            memset(base, 0, (size - vector->parent.size) * vector->parent.dsize);
        }

        vector->parent.size = size;
    }

    return 1;
}

int vector_push_back(vector_t vector, void* data)
{
    /* validity check */
    if (!vector) return 0;
    return vector_insert(vector, vector->parent.size, data, 1);
}

int vector_pop_back(vector_t vector)
{
    /* validity check */
    if (!vector) return 0;
    return vector_erase(vector, vector->parent.size - 1, vector->parent.size - 1);
}

int vector_insert(vector_t vector, int index, void* data, int num)
{
    int i = 0;
    unsigned char *op_ptr = NULL;
    int old_size = 0;

    /* validity check */
    if (!vector) return 0;

    if (index > vector->parent.size || index < 0)
    {
        return 0;
    }

    if (num < 0)
    {
        return 0;
    }

    old_size = vector->parent.size;
    if (vector_resize(vector, vector->parent.size + num) == 0)
    {
        return 0;
    }

    /* the last part of the data moves back to the inserted data to make room */
    op_ptr = (unsigned char *)vector->parent.base;
    if (index < old_size)
    {
        memmove((op_ptr + (index + num) * (vector->parent.dsize)), (op_ptr + index * (vector->parent.dsize)), vector->parent.dsize * (old_size - index));
    }

    /* assign new data */
    op_ptr = (unsigned char *)(vector->parent.base) + index * vector->parent.dsize;
    if (data != NULL)
    {    
        for (i = 0; i < num; i++)
        {
            memcpy(op_ptr + i * vector->parent.dsize, data, vector->parent.dsize);
        }
    }

    return 1;
}

int vector_erase(vector_t vector, int begin, int end)
{
    unsigned char *op_ptr = NULL;

    /* validity check */
    if (!vector) return 0;

    if (vector->parent.size < 1)
    {
        return 0;
    }

    if (begin > end || end >= vector->parent.size || begin < 0)
    {
        return 0;
    }

    /* move the later data forward to overwrite the deletion */
    op_ptr = (unsigned char *)vector->parent.base;
    memmove((op_ptr + begin * (vector->parent.dsize)), (op_ptr + (end + 1) * (vector->parent.dsize)), vector->parent.dsize * (vector->parent.size - end - 1));

    /* resize */
    vector_resize(vector, vector->parent.size - (end + 1 - begin));

    return 1;
}

int vector_clear(vector_t vector)
{
    /* validity check */
    if (!vector) return 0;
    return vector_erase(vector, 0, vector->parent.size - 1);
}

int vector_size(vector_t vector)
{
    /* validity check */
    if (!vector) return 0;
    return vector->parent.size;
}

int vector_empty(vector_t vector)
{
    /* validity check */
    if (!vector) return 0;
    return vector->parent.size==0?1:0;
}

int vector_capacity(vector_t vector)
{
    /* validity check */
    if (!vector) return 0;
    return vector->capacity;
}

void* vector_iterator_begin(vobject_t object)
{
    if (!object) return NULL;
    return ((vector_t)object)->parent.base;
}

void* vector_iterator_end(vobject_t object)
{
    if (!object) return NULL;
    return (void *)((unsigned char *)(((vector_t)object)->parent.base) + (((vector_t)object)->parent.size - 1) * ((vector_t)object)->parent.dsize);
}

void* vector_iterator_move(vobject_t object, void *node, int step)
{
    char *temp = ((char *)node) + step * ((vector_t)object)->parent.dsize;
    return (void *)temp;
}

void vector_iterator_nfs(void *node, void **first, void **second)
{
    if (!node) return;
    if (first) *first = NULL;
    if (second) *second = node;
}
